﻿using System.Collections.ObjectModel;
using System.IO.Ports;
using System.IO;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Data.Sqlite;
using System.Collections.Concurrent;
using System.Security.Cryptography;
using Microsoft.Win32;
using SimpleTool.SerialTool.SerialControl;
using Serilog;
using System.Windows;
using System.Windows.Media;
using static SimpleTool.Server.IDirFileServer;
using System.Windows.Controls;
using System.ComponentModel;
using System.Reflection;
using SimpleTool.ActivityUnit;
using OfficeOpenXml;
using SimpleTool.TransformComponent;
using Supabase.Postgrest.Models;
using Supabase.Postgrest.Attributes;
using System.Threading.Tasks;
using DynamicData;
using System.Windows.Input;
using Serilog.Core;
using Supabase.Realtime.PostgresChanges;
using Supabase.Realtime.Interfaces;
using System.Net.Sockets;
using System.Text.Json;
using static Supabase.Realtime.PostgresChanges.PostgresChangesOptions;
using System.CodeDom;
using static SimpleTool.SerialTool.SerialControl.ConfigSectionModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using Supabase.Realtime.Models;
using System.Threading.Channels;
using Microsoft.Extensions.DependencyInjection;

namespace SimpleTool.Server
{
    public class PowerPlantManeger : IPowerPlant, IDisposable
    {
        public PowerPlantManeger()
        {
            EventAddWatcher.EventArrived += InvokeCallBack;
            EventDelWatcher.EventArrived += InvokeCallBack;
        }

        private readonly ManagementEventWatcher EventAddWatcher = new (new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType=2"));

        private readonly ManagementEventWatcher EventDelWatcher = new (new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType=3"));

        private Action? _CallBack;

        public int LastEventSendTime = -1;

        public delegate void ReceiveBuffContent(string conent);

        public readonly SerialPort SerialEngine = new();

        public ILogger Logger = Log.ForContext<DatabaseServerManager>();

        public ReceiveBuffContent? ReceiveAction;

        private readonly object _syncobj = new();

        public string CacheOutputContent = string.Empty;

        public int CacheOutputMaxadd = 0;

        public bool _disposed = false;

        public bool _preciPice = false;

        public SerialPort GetSerialPortiInstance()
        {
            SerialEngine.ReadTimeout = 2;
            SerialEngine.WriteTimeout = 2;
            return SerialEngine;
        }

        public void InvokeCallBack(object sender, System.Management.EventArrivedEventArgs e)
        {
            _CallBack?.Invoke();
        }

        public void Moniter(Action Out, int type = 1, bool isOpen = false)
        {
            _CallBack = Out;

            switch (type)
            {
                case 0:
                    if (isOpen) 
                    {
                        EventDelWatcher.Start();
                    }
                    else
                    {
                        EventDelWatcher.Stop();
                    }
                    break;

                case 1:
                    if (isOpen)
                    {
                        EventAddWatcher.Start();
                    }
                    else
                    {
                        EventAddWatcher.Stop();
                    }
                    break;
            }
        }

        public bool Execute(string order)
		{
            if (SerialEngine.IsOpen)
            {
                try
                {
                    Logger.DebudElite(order.Replace("\r", "\\r").Replace("\n", "\\n"));
                    ReceiveAction?.Invoke(order);
                    SerialEngine.Write(order + "\r\n");
                }
                catch (Exception error)
                {
                    Logger.DebudElite($"seral order execute error: {error.Message}");
                }
                return true;
            }
            else
            {
                return false;
            }
		}

		public async void Write(List<CommandSectionModel.CommandStruct> pushs)
        {
            if (SerialEngine.IsOpen)
            {
                foreach (var commandinfos in pushs)
                {
                    string command = commandinfos.Command;
                    await Task.Delay(commandinfos.Duration);
                    Execute(command);
                }
            }
        }

        public (bool, string) Action(int stat = 1)
        {
            bool returnFlag = false;
            try
            {
                switch (stat)
                {
                    case 0:
                        if (SerialEngine.IsOpen)
                        {
                            SerialEngine.Close();
                            if (!SerialEngine.IsOpen)
                            {
                                returnFlag = true;
                                Subscribe(false);
                            }
                        }
                        break;

                    case 1:
                        if (SerialEngine.IsOpen) SerialEngine.Close();
                        SerialEngine.Open();
                        if (SerialEngine.IsOpen)
                        {
                            returnFlag = true;
                            Subscribe();
                        }
                        break;

                    case 2:
                        returnFlag = SerialEngine.IsOpen;
                        break;
                }
            }catch (Exception error)
            {
                Logger.Error($"{this} action error: {error.Message}");
                return (false, error.Message);
            }
            return (returnFlag, string.Empty);
		}
		
        public void Subscribe(bool isSubscribe = true)
		{
			if (isSubscribe)
            {
				SerialEngine.DataReceived += ReceiveSerialOutPut;
			}
            else
            {
				SerialEngine.DataReceived -= ReceiveSerialOutPut;
			}
            Logger.Debug($"subscribe stat {isSubscribe}");
		}

		public void Read<T>(ReceiveBuffContent action)
        {
            ReceiveAction = action;
            Logger.Debug($"out put change {typeof(T).Name}");
        }

        private void ReceiveSerialOutPut(object sender, SerialDataReceivedEventArgs e)
        {
            string readContent = SerialEngine.ReadExisting();
            //Logger.DebudElite("设备输出信息（初始）: ", readContent.Replace("\r", "\\r").Replace("\n", "\\n"));
            BuffAt(readContent);
        }

        private async void BuffAt(string readContent)
        {
            await Task.Run(() =>
            {
                int lenght = readContent.Length;
                int index = -1;
                MatchCollection matchs = Regex.Matches(readContent, "[\r\n|\n\r|\n]+");
                if (matchs.Count > 0) index = matchs.Select(x => x.Index).Max();
                bool _end = index != -1 && index >= lenght - 3 && Regex.Match(readContent.Substring(index), "[^\n\r]").Length == 0;

                if (_end || CacheOutputMaxadd >= 5)
                {
                    CacheOutputContent += readContent;
                    ReceiveAction?.Invoke(CacheOutputContent);
                    CacheOutputContent = string.Empty;
                    CacheOutputMaxadd = 0;
                }
                else
                {
                    CacheOutputContent += readContent;
                    CacheOutputMaxadd++;
                }
            });
        }

        public void BuffClear()
        {
            SerialEngine.DiscardInBuffer();
            SerialEngine.DiscardOutBuffer();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            bool _synclock = false;

            try
            {
                Monitor.Enter(_syncobj, ref _synclock);
                if (!_disposed && _synclock)
                {
                    if (disposing)
                    {
                        Subscribe(false);
                        EventAddWatcher.EventArrived -= InvokeCallBack;
                        EventDelWatcher.EventArrived -= InvokeCallBack;
                        Action(0);
                        SerialEngine.Dispose();
                    }
                    _disposed = true;
                }
            }
            finally
            {
                if (_synclock) Monitor.Exit(_syncobj);
                Logger.Debug($"{this} -> release, where lock stat {_synclock}");
            }
        }

        ~PowerPlantManeger()
        {
            Dispose(false);
        }
    }

    public class SerialConfigServerManager : ISerialServer
    {
        public ObservableCollection<string> ReceiveSerialInfo(EmSerialInfoType type)
        {
            ObservableCollection<string> SerialAttributes = [];
            switch (type) {

                case EmSerialInfoType.BundRate:
                    int[] commonBaudRates =
                    [
                        300,
                        600,
                        1200,
                        2400,
                        4800,
                        9600,
                        14400,
                        19200,
                        38400,
                        57600,
                        115200,
                        128000,
                        256000,
                        500000,
                        921600,
                        1000000,
                        2000000
                    ];
                    foreach (int baud in commonBaudRates)
                    {
                        string c_baud = baud.ToString();
                        SerialAttributes.Add(c_baud);
                        ConfigSectionModel.SerialConfigInfoMap.Com.SerialComBaudMap.TryAdd(c_baud, baud);
                    }
                    break;

                case EmSerialInfoType.StopBits:
                    foreach (StopBits stop in Enum.GetValues(typeof(StopBits)))
                    {
                        if (stop != StopBits.None)
                        {
                            string c_stop = stop.ToString();
                            SerialAttributes.Add(c_stop);
                            ConfigSectionModel.SerialConfigInfoMap.Com.SerialComStopMap.TryAdd(c_stop, stop);
                        }
                    }
                    break;

                case EmSerialInfoType.Databits:
                    foreach (int data in new List<int> { 5, 6, 7, 8 }) 
                    {
                        string c_data = data.ToString();
                        SerialAttributes.Add(c_data);
                        ConfigSectionModel.SerialConfigInfoMap.Com.SerialComDataMap.TryAdd(c_data, data);
                    }
                    break;

                case EmSerialInfoType.ParityBits:
                    foreach (Parity parity in Enum.GetValues(typeof(Parity)))
                    {
                        string c_parity = parity.ToString();
                        SerialAttributes.Add(c_parity);
                        ConfigSectionModel.SerialConfigInfoMap.Com.SerialComParityMap.TryAdd(c_parity, parity);
                    }
                    break;

                case EmSerialInfoType.HandShakes:
                    foreach (Handshake hand in Enum.GetValues(typeof(Handshake)))
                    {
                        string c_hand = hand.ToString();
                        SerialAttributes.Add(c_hand);
                        ConfigSectionModel.SerialConfigInfoMap.Com.SerialComHandShakeMap.TryAdd(c_hand, hand);
                    }
                    break;
            }
            return SerialAttributes;
        }
    }

    public class DatabaseServerManager(IDirFileServer _dirfileSeriver,
                                       IDenCryptoGraphic _dencryptServer) : IDatabaseServer, IDisposable
    {
        class UserPresence : BasePresence
        {
            [JsonProperty("cursorX")]
            public bool IsTyping { get; set; }
            [JsonProperty("onlineAt")]
            public DateTime OnlineAt { get; set; }
        }

        public ILogger Logger = Log.ForContext<DatabaseServerManager>();

        public IDirFileServer dirfileSeriver = _dirfileSeriver;

        public IDenCryptoGraphic dencryptServer = _dencryptServer;

        public required Dictionary<string, SqliteConnection> DBLocalManager = [];

        public required Dictionary<string, Supabase.Client> DBCloudManager = [];

        private string SupabaseAddreass = "https://hvoildbpqjyqvfptcjzz.supabase.co";

        private string SupabasePassWord = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imh2b2lsZGJwcWp5cXZmcHRjanp6Iiwicm9sZSI6ImFub24iLCJpYXQiO" +
                                          "jE3NDc3OTU3MDIsImV4cCI6MjA2MzM3MTcwMn0.-jqZO95mNPrBfqWcbIti7pjRTohvfDp6j1a_0HK8FuU";

        private string LocalDBpath = Directory.GetCurrentDirectory();

        private string LocalDBkey = "Auto";

        private string DBKey = string.Empty;

        private bool CloudIdentify = true;

        private readonly Dictionary<string, List<string>> CacheCloudAnchormMap = [];

        private readonly List<Supabase.Realtime.RealtimeChannel> RealtimeChannels = [];

        private readonly string safetyKey = "CryptoGraphicToregistry";

        private readonly SemaphoreSlim _asyncLock = new (1, 1);

        public delegate void RealtimeChannelBuff();

        public Action<string, string>? RealtimeChannel = (name, type) => { };

        public bool GetIdentify()
        {
            return CloudIdentify;
        }

        public bool AlterLocalDBkey(string key)
        {
            if (DBLocalManager != null)
            {
                if (DBLocalManager.ContainsKey(key))
                {
                    DBKey = key;
                    return true;
                }
            }
            return false;
        }

        public KeyValuePair<string, string> GetImportantInfo(bool cloud = true)
        {
            if (cloud)
            {
                return new KeyValuePair<string, string>(SupabaseAddreass, SupabasePassWord);
            }
            else
            {
                return new KeyValuePair<string, string>(LocalDBpath, LocalDBkey);
            }
        }

        public void SetImportantInfo(KeyValuePair<string, string> info, bool cloud = true)
        {
            if (cloud)
            {
                SupabaseAddreass = info.Key;
                SupabasePassWord = info.Value;
            }
            else
            {
                LocalDBpath = info.Key;
                LocalDBkey = info.Value;
            }
        }

        private static string FrcName(string key)
        {
            string expectkey = string.Empty;
            List<string> splitkey = [.. key.Split('.')];
            if (0 < splitkey.Count)
            {
                expectkey = splitkey[0];
            }
            return expectkey;
        }

        public bool LocalDBSurvival(string dbkey)
        {
            foreach (var dict in DBLocalManager)
            {
                if (FrcName(dict.Key) == FrcName(dbkey) && dict.Value.State == System.Data.ConnectionState.Open)
                {
                    return true;
                }
                else
                {
                    try
                    {
                        dict.Value.Open();
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                }
            }
            return false;
        }

        public async Task<SqliteExceuteResult> LocalDBQuery(List<string> sqls)
        {
            SqliteExceuteResult exceuteResult = new ();
            try
            {
                Dictionary<string, Dictionary<string, List<object>>> TotalResult = [];
                if (DBLocalManager.TryGetValue(DBKey, out SqliteConnection? connect))
                {
                    foreach (var sql in sqls)
                    {
                        await _asyncLock.WaitAsync();
                        using var transation = connect.BeginTransaction(deferred: true);
                        try
                        {
                            string privious = Regex.Split(sql, "\\s+").GetSafetyChar(0).Trim();
                            var command = connect.CreateCommand();
                            command.Transaction = transation;
                            command.CommandText = sql;
                            if (privious.Contains("select", StringComparison.InvariantCultureIgnoreCase))
                            {
                                using SqliteDataReader reader = command.ExecuteReader();
                                List<string> columnNames = [.. Enumerable.Range(0, reader.FieldCount).Select(reader.GetName)];
                                Dictionary<string, List<object>> secResult = [];
                                while (reader.Read())
                                {
                                    foreach (string field in columnNames)
                                    {
                                        if (secResult.TryGetValue(field, out List<object>? value))
                                        {
                                            value.Add(reader[field]);
                                            secResult[field] = value;
                                        }
                                        else
                                        {
                                            secResult.TryAdd(field, [reader[field]]);
                                        }
                                    }
                                }
                                if (secResult.Count > 0) TotalResult.TryAdd(sql, secResult);
                            }
                            else if (privious.Contains("update", StringComparison.InvariantCultureIgnoreCase) ||
                                     privious.Contains("insert", StringComparison.InvariantCultureIgnoreCase) ||
                                     privious.Contains("delete", StringComparison.InvariantCultureIgnoreCase) ||
                                     privious.Contains("create", StringComparison.InvariantCultureIgnoreCase))
                            {
                                command.ExecuteNonQuery();
                            }
                            transation.Commit(); // out of loop, because it release the transation
                        }
                        catch (Exception error)
                        {
                            transation.Rollback();
                            Logger.Error($"sql action error: {error.Message} {sql}");
                            exceuteResult.ErrorHint = error.Message;
                        }
                        finally
                        {
                            _asyncLock.Release();
                        }
                    }
     
                    exceuteResult.Result = TotalResult;
                }
                else
                {
                    Logger.Error("database is not open");
                    exceuteResult.ErrorHint = "Fail";
                }
            }
            catch (Exception error)
            {
                Logger.Error($"sql outset error: {error.Message}");
                exceuteResult.ErrorHint = error.Message;
            }
            return exceuteResult;
        }

        /// <summary>
        /// main key-> sql;
        /// secondary key-> table field;
        /// secondary values-> table values collection;
        /// </summary>
        /// <param name="sqls"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public async Task<SqliteExceuteResult> LocalDBQueryFree(params string[] sqls)
        {
            SqliteExceuteResult exceuteResult = new SqliteExceuteResult();
            exceuteResult = await LocalDBQuery([.. sqls]);
            return exceuteResult;
        }

        /// <summary>
        /// default dir-> exec diretory;
        /// default name-> database;
        /// default password-> none;
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <param name="password"></param>
        /// <param name="way"></param>
        /// <exception cref="ArgumentException"></exception>
        public async void InitLocalDB(SqliteConnectionConfig Configurate)
        {
            List<string> connectPatams = [];
            CloudIdentify = false;
            string dbp;
            if (string.IsNullOrEmpty(Configurate.Path))
            {
                dbp = "DataSource" + "=" + $"{Directory.GetCurrentDirectory()}/database.db";
            }
            else
            {
                if (File.Exists(Configurate.Path))
                {
                    dbp = "DataSource" + "=" + Configurate.Path;
                }
                else if (Directory.Exists(Configurate.Path))
                {
                    if (string.IsNullOrEmpty(Configurate.Name))
                    {
                        dbp = "DataSource" + "=" + $"{Configurate.Path}/database.db";
                    }
                    else
                    {
                        dbp = "DataSource" + "=" + $"{Configurate.Path}/{Configurate.Name}.db";
                    }
                }
                else
                {
                    dirfileSeriver.CreateDir(Configurate.Path);
                    dbp = $"DataSource" + "=" + $"{Configurate.Path}/database.db";
                }
            }
            connectPatams.Add(dbp); DBKey = dirfileSeriver.GetSimpleName(dbp);

            string verityPassword = dirfileSeriver.RegistryAction(1, RegisterPath.HKEY_CURRENT_USER_Software, "SimpleTool", DBKey);
            string encryptKey = string.Empty;
            if (string.IsNullOrEmpty(verityPassword))
            {
                if (!string.IsNullOrEmpty(Configurate.EncryptValue) && !string.IsNullOrEmpty(Configurate.EncryptKey))
                {
                    await Task.Run(() =>
                    {
                        encryptKey = dencryptServer.EnCryptoGraphic(Configurate.EncryptValue, Configurate.EncryptKey);
                    }).ConfigureAwait(false);

                    connectPatams.Add("Password" + "=" + encryptKey);
                }
                else
                {
                    throw new ArgumentException("encrypt info is incomplete");
                }
            }
            else
            {
                string deReworkKey =string.Empty;
                if (!string.IsNullOrEmpty(Configurate.Password))
                {
                    deReworkKey = Configurate.Password;
                }
                else
                {
                    await Task.Run(() =>
                    {
                        deReworkKey = dencryptServer.DeCryptoGraphic(verityPassword, safetyKey);
                    }).ConfigureAwait(false);
                }
                connectPatams.Add("Password" + "=" + deReworkKey);
                Logger.Information("decrypt registry key: " + deReworkKey);
            }
            connectPatams.Add("Mode" + "=" + $"{Configurate.Way}"); 
            try
            {
                SqliteConnection connect = new SqliteConnection(string.Join(";", connectPatams));
                connect.Open();
                if (connect.State == System.Data.ConnectionState.Open)
                {
                    DBLocalManager.TryAdd(DBKey, connect);
                    DBLocalManager[DBKey].DefaultTimeout = 1;
                    if (string.IsNullOrEmpty(encryptKey))
                    {
                        await LocalDBQueryFree(
                            $"PRAGMA key = '{encryptKey}'" +
                             "PRAGMA cipher_compatibility = 4;" +
                             "PRAGMA kdf_iter = 2560;" +
                             "PRAGMA cipher_use_hmac = ON;" +
                             "PRAGMA cipher_page_size = 4096;" +
                             "PRAGMA busy_timeout = 2000;"
                            );
                        //cipher_compatibility SQLCipher 3 兼容性
                        //kdf_iter 设置密钥派生函数的迭代次数
                        //cipher_use_hmac 启用HMAC校验
                        //cipher_page_size 设置加密页面大小
                        if (string.IsNullOrEmpty(verityPassword))
                        {
                            await Task.Run(() =>
                            {
                                string reworkKey = dencryptServer.EnCryptoGraphic(encryptKey, safetyKey);
                                dirfileSeriver.RegistryAction(0, RegisterPath.HKEY_CURRENT_USER_Software, "SimpleTool", DBKey, reworkKey);
                            }).ConfigureAwait(false);

                            Logger.Debug("lock value of database in registry");
                        }
                    }
                    Logger.Debug($"db is connect, where dbkey: {DBKey}");
                }
                else
                {
                    Logger.Debug($"db open fail, where dbkey: {DBKey}");
                }
                
            }
            catch (Exception e)
            {
                throw new ArgumentException($"database open error, hint: {e.Message}");
            }
        }

        public async Task<bool> InitCloudDB(string address, string key)
        {
            async void CloudAnchorChannel()
            {
                int maxRetry = 3; // 监听遭遇错误时的重试次数
                // 处理监听返回数据
                Action<string, PostgresChangesResponse> response = (name, e) =>
                {
                    PostgresChangesPayload<Supabase.Realtime.Socket.SocketResponsePayload>? Payload = e.Payload;
                    if (Payload is not null)
                    {
                        Supabase.Realtime.Socket.SocketResponsePayload<Supabase.Realtime.Socket.SocketResponsePayload>? response = Payload.Data;
                        if (response is not null && response.Table is not null && response._type is not null)
                        {
                            RealtimeChannel?.Invoke(name, response._type);
                        }
                    }
                };

                while (maxRetry > 0)
                {
                    try
                    {
                        //所有表监听，并添加监听句柄至字典

                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<ProductConfigField>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(ProductConfigField), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<ProductConfigRelation>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(ProductConfigRelation), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<WorkOrderUnfold>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(WorkOrderUnfold), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<WorkOrderPreview>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(WorkOrderPreview), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<DbFieldRelation>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(DbFieldRelation), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<SerialConfig>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(SerialConfig), change);
                        }));
                        RealtimeChannels.Add(await DBCloudManager["CloudDB"].From<SerialCommand>().On(ListenType.All, (sender, change) =>
                        {
                            response(nameof(SerialCommand), change);
                        }));
                        break;
                    } 
                    catch (Exception error)
                    {
                        Logger.ErrorElite($"Realtime connect error: {error.Message}");
                    }
                    maxRetry--;
                }
            }

            try
            {
                CloudIdentify = true;
                if (DBCloudManager.TryGetValue("CloudDB", out Supabase.Client? connection))
                {
                    return true;
                }
                else
                {
                    var options = new Supabase.SupabaseOptions
                    {
                        AutoConnectRealtime = true,
                    };
                    var supabase = new Supabase.Client(address, key, options);

                    var isconnect = await supabase.InitializeAsync();
                    if (isconnect is not null)
                    {
                        DBCloudManager.TryAdd("CloudDB", supabase);
                        CloudAnchorChannel();
                        return true;
                    }
                }
            } 
            catch (Exception error)
            {
                Logger.DebudElite($"cloud db connect fail: {error.Message}");
            }
            return false;
        }

        public void CloudAnchorChannel<Table>(Action execute, string? type = null)
        {
            string table = typeof(Table).Name;
            bool watchState = false;
            Stopwatch now = Stopwatch.StartNew();

            RealtimeChannel = (_table, _type) =>
            {
                Logger.WarningElite($"table {table} -> {_type} action transit");

                if (table == _table)
                {
                    if ((type is not null && _type == type) || type == null)
                    {
                        watchState = true;
                        execute.Invoke();
                    }
                }
            };

            if (now.ElapsedMilliseconds > 8000 && !watchState) execute.Invoke();
        }

        public Supabase.Client? GetCloudDB()
        {
            if (DBCloudManager.TryGetValue("CloudDB", out Supabase.Client? connection))
            {
                return connection;
            }
            return null;
        }

        public void TemporaryRelease(bool isCloud = true)
        {
            if (isCloud)
            {
                if (DBLocalManager.TryGetValue(DBKey, out SqliteConnection? connection))
                {
                    connection?.Close();
                    connection?.Dispose();
                    DBLocalManager.Remove(DBKey);
                }
            }
            else
            {
                if (DBCloudManager.TryGetValue("CloudDB", out Supabase.Client? connection))
                {
                    foreach(var i in RealtimeChannels)
                    {
                        i.Unsubscribe();
                    }
                    connection.Realtime.Disconnect();
                    DBCloudManager.Remove("CloudDB");
                }
            }
        }

        private readonly object _syncobj = new();

        public bool _disposed = false;
        /// <summary>
        /// release occupy source
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            bool _synclock = false;

            try
            {
                if (!_disposed)
                {
                    Monitor.Enter(_syncobj, ref _synclock);
                    if (DBLocalManager != null && disposing && _synclock)
                    {
                        foreach (var key in DBLocalManager.Keys)
                        {
                            DBLocalManager[key].Close();
                            DBLocalManager[key].Dispose();
                        }
                        DBLocalManager.Clear();
                    }
                    _disposed = true;
                }
            }
            finally
            {
                if (_synclock) Monitor.Exit(_syncobj);
                Logger.Debug($"{this} -> release, where lock stat {_synclock}");
            }
        }

        public async Task<bool> CheckField<FiedType>(string table, string field, FiedType value)
        {
            await _asyncLock.WaitAsync();
            bool check = false;
            if (DBLocalManager.TryGetValue(DBKey, out SqliteConnection? connect))
            {
                using var transation = connect.BeginTransaction(deferred: true);
                try
                {
                    var command = connect.CreateCommand();
                    command.Transaction = transation;
                    command.CommandText = $"SELECT {field} FROM {table}";
                    using SqliteDataReader reader = command.ExecuteReader();
                    List<object> collection = [];
                    while (reader.Read())
                    {
                        collection.Add(reader[field]);
                    }
                    transation.Commit();
                    if (value is not null && collection.Contains(value))
                    {
                        check = true;
                    }
                    else check = false;
                }
                catch
                {
                    check = false;
                }
            }
            else check = false;
            _asyncLock.Release();

            return check;
        }

        ~DatabaseServerManager()
        {
            Dispose(false);
        }

    }

    public class DirFileServerManager : IDirFileServer, IDisposable
    {
        public ILogger Logger = Log.ForContext<DatabaseServerManager>();

        private CancellationTokenSource TokenSource;

        public DirFileServerManager()
        {
            TokenSource = new();
        }
        public async Task ReadAsync(string file, ReceiveLine receiveLine)
        {
            string? line;
            using FileStream fstream = new FileStream(file, FileMode.Open, FileAccess.Read);
            using StreamReader rstream = new StreamReader(fstream);
            while ((line = await rstream.ReadLineAsync()) != null)
            {
                receiveLine.Invoke(line);
            }
        }

        public async Task<List<string>> ReadAsync(string file)
        {
            List<string> collections = [];
            using FileStream fstream = new FileStream(file, FileMode.Open, FileAccess.Read);
            using StreamReader rstream = new StreamReader(fstream);
            await Task.Run(async () =>
            {
                
                while (!rstream.EndOfStream)
                {
                    string? line = await rstream.ReadLineAsync();
                    if (line != null) collections.Add(line);
                }
            });
            return collections;
        }

        public List<string> Read(string file)
        {
            List<string> collections = [];
            using FileStream fstream = new FileStream(file, FileMode.Open, FileAccess.Read);
            using StreamReader rstream = new StreamReader(fstream);

            while (!rstream.EndOfStream)
            {
                string? line = rstream.ReadLine();
                if (line != null) collections.Add(line);
            }
            
            return collections;
        }

        public bool CreateDir(string dir)
        {
            string templatePath = string.Empty;
            if (Directory.Exists(dir)) return true;
            Regex regexpPath = new("[/|\\\\]"); 
            List<string> splitPath = [.. regexpPath.Split(dir)];
            if (splitPath.Count > 0)
            {
                for (int digit = 1; digit < splitPath.Count; digit++)
                {
                    templatePath = string.Join("/", splitPath.GetRange(0, digit + 1)); // range -1, fill index
                    if (!Directory.Exists(templatePath)) Directory.CreateDirectory(templatePath);
                }
                if (Directory.Exists(templatePath)) return true;
            }
            return false;
        }

        public bool CreateFile(string file)
        {
            string templatePath = string.Empty;
            if (File.Exists(file)) return true;
            Regex regexpPath = new("[/|\\\\]");
            List<string> splitPath = [.. regexpPath.Split(file)];
            if (splitPath.Count > 0)
            {
                for (int digit = 1; digit < splitPath.Count; digit++)
                {
                    templatePath = string.Join("/", splitPath.GetRange(0, digit + 1)); // range -1, fill index
                    if (IsDir(templatePath))
                    {
                        if (!CreateDir(templatePath)) Directory.CreateDirectory(templatePath);
                    } else
                    {
                        if (!File.Exists(templatePath)) File.Create(templatePath);
                    }
                }
                if (File.Exists(templatePath)) return true;
            }
            return false;
        }

        public string GetDir(string file)
        {
            if (!File.Exists(file)) return string.Empty;
            Regex regexpPath = new("[/|\\\\]");
            List<string> splitPath = [.. regexpPath.Split(file)];
            return string.Join("/", splitPath.GetRange(0, splitPath.Count - 1));
        }

        /// <summary>
        /// cancellection the getlocalfiles action
        /// </summary>
        public async void GetLocalFilesCancelletion(bool isDispose = false)
        {
            TokenSource.Cancel();
            await Task.Delay(1000).ContinueWith((task) =>
            {
                TokenSource = new CancellationTokenSource();
            });
        }

        /// <summary>
        /// mode 0: exact match  1: blur match
        /// </summary>
        /// <param name="path"></param>
        /// <param name="extension"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<List<string>> GetLocalFilesAsync(
            string path, 
            List<string> keyworks, 
            List<string> extensions,
            Action<string> repaiddata,
            sbyte mode = 0)
        {
            return await GetLocalFilesAsync([path], keyworks, extensions, repaiddata, mode);
        }

        public void GetLocalFilesAsyncTovoid(
            string path,
            List<string> keyworks,
            List<string> extensions,
            Action<string> repaiddata,
            sbyte mode = 0)
        {
            _= GetLocalFilesAsync([path], keyworks, extensions, repaiddata, mode);
        }

        /// <summary>
        /// mode 0: exact match  1: blur match
        /// </summary>
        /// <param name="path"></param>
        /// <param name="extension"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<List<string>> GetLocalFilesAsync(
            List<string> paths, 
            List<string> keyworks, 
            List<string> extensions,
            Action<string> repaiddata,
            sbyte mode = 0)
        {
            Progress<string> LocalFilesProgress = new(repaiddata);
            return await RunLocalFiles(paths, keyworks, extensions, LocalFilesProgress, mode);
        }
        private async Task<List<string>> RunLocalFiles(
                    List<string> paths,
                    List<string> keyworks,
                    List<string> extensions,
                    IProgress<string> progress,
                    sbyte mode = 0)
        {
            ConcurrentBag<string> files = [];

            var ParallelOption = new ParallelOptions
            {
                CancellationToken = TokenSource.Token,
                MaxDegreeOfParallelism = 3
            };

            try
            {
                await Task.Run(() =>
                {
                    _ = Parallel.ForEach(paths, ParallelOption, path =>
                    {
                        if (System.IO.Path.Exists(path))
                        {
                            TokenSource.Token.ThrowIfCancellationRequested();
                            ParallelOption.CancellationToken.ThrowIfCancellationRequested();

                            IEnumerable<string> receives = Directory.EnumerateFiles(path, ".", SearchOption.AllDirectories)
                            .AsParallel()
                            .WithCancellation(TokenSource.Token);

                            foreach (var receive in receives)
                            {
                                if (ConditionMatch(receive, keyworks, extensions, mode))
                                {
                                    TokenSource.Token.ThrowIfCancellationRequested();
                                    progress?.Report(receive);
                                    files.Add(receive);
                                }
                            }
                        }
                        else
                        {
                            progress?.Report(string.Empty);
                        }
                    });
                }, TokenSource.Token);

            }
            catch (OperationCanceledException)
            {
                GetLocalFilesCancelletion();
            }
            return [.. files];
        }
        private bool ConditionMatch(
            string file,
            List<string> keyworks,
            List<string> extensions,
            sbyte mode)
        {
            sbyte total = 0;
            sbyte count = 0;
            bool ckey = keyworks.Count > 0;
            bool cexd = extensions.Count > 0;
            if (ckey) total++;
            if (cexd) total++;
            if (ckey)
            {
                foreach (var keywork in keyworks)
                {
                    TokenSource.Token.ThrowIfCancellationRequested();
                    if (mode == 0)
                    {
                        if (file.Contains(keywork)) count++;
                    }
                    else if (mode == 1)
                    {
                        if (file.Contains(keywork, StringComparison.CurrentCultureIgnoreCase)) count++;
                    }
                }
            }
            if (cexd)
            {
                foreach (var exd in extensions)
                {
                    TokenSource.Token.ThrowIfCancellationRequested();
                    if (mode == 0)
                    {
                        if (GetSuffix(file).Contains(exd)) count++;
                    }
                    else if (mode == 1)
                    {
                        if (GetSuffix(file).Contains(exd, StringComparison.CurrentCultureIgnoreCase)) count++;
                    }
                }
            }
            if (count == total)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<Dictionary<string, Dictionary<string, List<string>>>> GetExcel(ExcelOptions options, ExcelOriented orient = ExcelOriented.ColumnOriented)
        {
            Dictionary<string, Dictionary<string, List<string>>> ExcelResult = [];
            ExcelPackage.License.SetNonCommercialPersonal("My_Private_App");
            await Task.Run(() =>
            {
                using (ExcelPackage ExcelEngin = new(new System.IO.FileInfo(options.file)))
                {
                    List<OfficeOpenXml.ExcelWorksheet> WorkSheets = [.. ExcelEngin.Workbook.Worksheets];
                    List<string> SheetNames = [.. WorkSheets.Select(x => x.Name)];
                    if (options.questionSheet)
                    {
                        CustomMessageBox.Invoke result = CustomMessageBox.Show(
                            "选择工单文件，支持多选", 
                            ["取消", "确定"], 
                            SheetNames, 
                            System.Windows.Controls.SelectionMode.Extended);
                        options.sheets = result.selectedValues;
                    }
                    else
                    {
                        if (SheetNames.Count > 0)
                        {
                            options.sheets.Add(SheetNames[0]);
                        }
                    }
                    for (int sRow = 0; sRow < WorkSheets.Count; sRow++)
                    {
                        ExcelWorksheet sheet = WorkSheets[sRow];

                        if (options.sheets.Count == 0 || options.indexs.Count == 0)
                        {
                            ProcessingSheet(sheet, ref ExcelResult, options, orient);
                        }
                        else
                        {
                            if (options.indexs.Contains(sRow) || options.sheets.Contains(sheet.Name))
                            {
                                ProcessingSheet(sheet, ref ExcelResult, options, orient);
                            }
                        }
                    }
                }
            }).ConfigureAwait(false);
            return ExcelResult;
        }

        private void ProcessingSheet(ExcelWorksheet sheet,
                                     ref Dictionary<string, Dictionary<string, List<string>>> result,
                                     ExcelOptions options,
                                     ExcelOriented orient = ExcelOriented.ColumnOriented)
        {
            int ExcelRows = sheet.Dimension.Rows;
            int ExcelColumns = sheet.Dimension.Columns;
            Dictionary<string, List<string>> SheetMap = [];

            if (orient == ExcelOriented.ColumnOriented)
            {

                for (int column = 1; column <= ExcelColumns; column++)
                {
                    string Title = string.Empty;
                    List<string> ColumnDatas = [];
                    for (int row = 1; row <= ExcelRows; row++)
                    {
                        string Value = sheet.Cells[row, column].Text;
                        if (row == 1)
                        {
                            Title = Value;
                        }
                        else
                        {
                            ColumnDatas.Add(Value);
                            string sheet_name = sheet.Name;
                            string column_name = Title;
                            double prograss = Math.Round((double)row / ExcelRows * 100, 2);
                            prograss = Math.Min(100, prograss);
                            System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
                            {
                                options.HintCallBack?.Invoke(sheet_name, column_name, prograss);
                            });

                            if (options.limit > 0 && options.limit == ColumnDatas.Count)
                            {
                                System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
                                {
                                    options.HintCallBack?.Invoke(sheet_name, column_name, 100.0);
                                });
                                break;
                            }
                        }
                    }

                    if (!string.IsNullOrEmpty(Title))
                    {
                        SheetMap.TryAdd(Title, ColumnDatas);
                    }
                }
                result.TryAdd(sheet.Name, SheetMap);
            }
            else
            {
                for (int row = 1; row <= ExcelRows; row++)
                {
                    string Title = string.Empty;
                    List<string> RowDatas = [];
                    for (int column = 1; column <= ExcelColumns; column++)
                    {
                        string Value = sheet.Cells[row, column].Text;
                        if (row == 1)
                        {
                            Title = "Title";
                            RowDatas.Add(Value);
                        }
                        else
                        {
                            RowDatas.Add(Value);
                            string sheet_name = sheet.Name;
                            string column_name = $"第 {column} 列";
                            double prograss = Math.Round((double)column / ExcelColumns * 100, 2);
                            prograss = Math.Min(100, prograss);
                            System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
                            {
                                options.HintCallBack?.Invoke(sheet_name, column_name, prograss);
                            });

                            if (options.limit > 0 && options.limit == RowDatas.Count)
                            {
                                System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
                                {
                                    options.HintCallBack?.Invoke(sheet_name, column_name, 100.0);
                                });
                                break;
                            }
                        }
                    }

                    if (string.IsNullOrEmpty(Title))
                    {
                        SheetMap.TryAdd($"{row}", RowDatas);
                    }
                    else
                    {
                        SheetMap.TryAdd(Title, RowDatas);
                    }
                }
                result.TryAdd(sheet.Name, SheetMap);
            }
        }

        public string GetSuffix(string file)
        {
            return System.IO.Path.GetExtension(file);
        }

        public string GetSimpleName(string file)
        {
            Regex regexpPath = new("[/|\\\\]");
            List<string> splitPath = [.. regexpPath.Split(file)];
            return splitPath[splitPath.Count - 1];
        }

        public bool IsDir(string dir)
        {
            if (Directory.Exists(dir))
            {
                return true;
            }
            return false;
        }

        public bool IsFile(string file)
        {
            if (File.Exists(file))
            {
                return true;
            }
            return false;
        }

        public string RegistryAction(int type, string path, string name, string key, string value="")
        {
            path = path + "\\" + name;
            return RegistryAction(type, path, key, value);
        }

        public string RegistryAction(int type, RegisterPath path, string name, string key, string value)
        {
            string realpath = string.Empty;
            switch (path)
            {
                case RegisterPath.HKEY_CURRENT_USER_Software:
                    realpath = "Software";
                    break;
                case RegisterPath.HKEY_CURRENT_USER_Microsoft:
                    realpath = "HKEY_CURRENT_USER\\Microsoft\\Windows";
                    break;
                default:
                    realpath = "Software";
                    break;
            }
            return RegistryAction(type, realpath, name, key, value);
        }

        public string RegistryAction(int type, string path, string key, string value)
        {
            string getValue = "";
            RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(path, true);
            switch (type)
            {
                case 0:
                    regKey ??= Registry.CurrentUser.CreateSubKey(path);
                    regKey.SetValue(key, value);
                    regKey.Close();
                    break;

                case 1:
                    if (regKey is not null)
                    {
                        object? regvalue = regKey.GetValue(key);
                        if (regvalue is not null)
                        {
                            getValue = regvalue.ToString() ?? string.Empty;
                        }
                        regKey.Close();
                    }
                    break;

                case 2:
                    if (regKey != null)
                    {
                        regKey.DeleteValue(key, false);
                        regKey.Close();
                    }
                    break;

                default:
                    regKey ??= Registry.CurrentUser.CreateSubKey(path);
                    regKey.SetValue(key, value);
                    regKey.Close();
                    break;

            }
            return getValue;
        }

        private readonly object _syncobj = new();

        public bool _disposed = false;
        /// <summary>
        /// release occupy source
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            bool _synclock = false;

            try
            {
                if (!_disposed)
                {
                    Monitor.Enter(_syncobj, ref _synclock);
                    if (TokenSource != null)
                    {
                        TokenSource.Dispose();
                    }
                    _disposed = true;
                }
            }
            finally
            {
                if (_synclock) Monitor.Exit(_syncobj);
                Logger.Debug($"{this} -> release, where lock stat {_synclock}");
            }
        }

        ~DirFileServerManager()
        {
            Dispose(false);
        }
    }

    public static class Extendsion
    {
        public static ILogger Logger = Log.ForContext<Exception>();

        public enum CollectionType
        {
            t_ObservableCollection,
            t_list,
            t_array
        };

        public enum StringTemplate
        {
            Com
        }

        public enum ExpectAction
        {
            Com
        }

        public class MatchInfo
        {
            int index;
            int end;
            public MatchInfo(int _index, int _end)
            {
                index = _index;
                end = _end;
            }

        }

        public static ICollection<T> SliceTInterable<T>(this ICollection<T> source, int StartIndex, int AdvanceIndex)
        {

            if (source is null) throw new ArgumentException("data source error");
            int safetyAdd = Math.Min(AdvanceIndex, source.Count() - StartIndex);
            if (source is T[] ADATA)
            {
                T[] ReceiveColletion = new T[safetyAdd];
                Array.Copy(ADATA, StartIndex, ReceiveColletion, 0, AdvanceIndex);
                return ReceiveColletion;
            } else if (source is List<T> LDATA) {
                List<T> Range = LDATA.GetRange(StartIndex, safetyAdd);
                return Range;
            } else
            {
                return (ICollection<T>)source.Skip(StartIndex).Take(safetyAdd);
            }
        }

        public static ICollection<string> Com(this ManagementObjectSearcher source, CollectionType Type)
        {
            ICollection<string> Collection;
            switch (Type)
            {
                case CollectionType.t_ObservableCollection:
                    Collection = new ObservableCollection<string>();
                    break;
                case CollectionType.t_array:
                    Collection = new List<string>().ToArray();
                    break;
                case CollectionType.t_list:
                    Collection = new List<string>();
                    break;

                default:
                    Collection = new List<string>();
                    break;
            }
            if (source is null) throw new ArgumentException("data source error");
            ConfigSectionModel.SerialConfigInfoMap.Com.SerialComNameMap.Clear();
            foreach (ManagementBaseObject coml in source.Get())
            {
                string Cominfo = coml["Name"].ToString() ?? string.Empty;
                if (Cominfo != null)
                {
                    string result = Regex.Match(Cominfo, @"(COM.*\d+)").Value;
                    if (result != null && result.Length > 0)
                    {
                        Collection.Add(Cominfo);
                        if (!ConfigSectionModel.SerialConfigInfoMap.Com.SerialComNameMap.ContainsKey(Cominfo))
                        {
                            ConfigSectionModel.SerialConfigInfoMap.Com.SerialComNameMap.TryAdd(Cominfo, result);
                        } 
                    }
                }
            }
            return Collection;
        }

        public static void DebudElite(this ILogger logger, params object[] args)
        {
            logger.Debug(string.Join("  ", args));
        }

        public static void ErrorElite(this ILogger logger, params object[] args)
        {
            logger.Error(string.Join("  ", args));
        }

        public static void WarningElite(this ILogger logger, params object[] args)
        {
            logger.Warning(string.Join("  ", args));
        }

        public static void FatalElite(this ILogger logger, params object[] args)
        {
            logger.Fatal(string.Join("  ", args));
        }

        public static void InfoElite(this ILogger logger, params object[] args)
        {
            logger.Information(string.Join("  ", args));
        }

        public static SolidColorBrush FindSolidBrush(this string source)
        {
            SolidColorBrush? seek = source.FindSource<SolidColorBrush>();
            if (seek is not null)
            {
                return seek;
            }
            
            return new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));
        }

        public static T? FindSource<T>(this string source)
        {
            if (System.Windows.Application.Current.Resources[source] is T d)
            {
                return d;
            }
            return default;
        }

        public static List<string> MatchAll(this string source, string regexp)
        {
            List<string> MatchsResult = [];
            MatchCollection matchCollection = Regex.Matches(source, regexp);
            foreach (Match match in matchCollection)
            {
                if (match.Groups.Count > 0)
                {
                    foreach (Group group in matchCollection)
                    {
                        if (!MatchsResult.Contains(group.Value))
                        {
                            MatchsResult.Add(group.Value);
                        }
                    }
                }
                else
                {
                    continue;
                }
            }
            return MatchsResult;
        }

        public static string GetDesorderly(this int source)
        {
            byte[] bytes = new byte[source];
            using var random = RandomNumberGenerator.Create();
            random.GetBytes(bytes);
            Regex replace = new ("\\W+");
            return replace.Replace(BitConverter.ToString(bytes), string.Empty);
        }
        
        public static string GetDesorderly(this IList<string> source)
        {
            string idkey = string.Empty;
            int resetCount = 0;
            int maxLen = source.Select(x => x.Length).Max() / 2;
            while (true)
            {
                idkey = maxLen.GetDesorderly();
                if (resetCount >= 400)
                {
                    maxLen++;
                    resetCount = 0;
                }
                if (!source.Contains(idkey)) break;
            }
            return idkey;
        }

        public static string GetDesorderly(this IList<object> source)
        {
            string idkey = string.Empty;
            int resetCount = 0;
            int maxLen = source.Select(x => x.GetSafetyChar().Length).Max() / 2;
            while (true)
            {
                idkey = maxLen.GetDesorderly();
                if (resetCount >= 400)
                {
                    maxLen++;
                    resetCount = 0;
                }
                if (!source.Select(x => x.GetSafetyChar()).Contains(idkey)) break;
            }
            return idkey;
        }

        public static string GetDesorderlyBlue(this int source, bool isScan = false)
        {
            byte[] bytes = new byte[source];
            using var random = RandomNumberGenerator.Create();
            random.GetBytes(bytes);
            Regex replace = new("\\W+");
            if (isScan)
            {
                return replace.Replace(BitConverter.ToString(bytes), "-");
            }
            else
            {
                return replace.Replace(BitConverter.ToString(bytes), ":");
            }
        }

        public static KType? GetSafetyKey<KType, VType>(this IDictionary<KType, VType> source, VType value)
        {
            KType? RealKey = default;
            foreach(KeyValuePair<KType, VType> pair in source)
            {
                if (pair.Value is not null && pair.Value.Equals(value))
                {
                    RealKey = pair.Key;
                }
            }
            return RealKey;
        }

        public static string Join(this IEnumerable<string> source, string space, bool isSqlColumn = false, List<string>? expects = null)
        {
            string _string = string.Empty;
            if (expects is not null) source = [.. source.Where(x => !expects.Contains(x))];
            List<string> sources = [.. source];
            if (sources.Count > 1)
            {
                int index = 0;
                foreach (var single in sources)
                {
                    string _single = single;

                    if (isSqlColumn)
                    {
                        _single = $"'{single}'";
                    }

                    if (index < sources.Count - 1)
                    {
                        _string += _single + space;
                    }
                    else
                    {
                        _string += _single;
                    }
                    index++;
                }
            }
            else
            {
                if (sources.Count == 1) _string = sources.GetSafetyChar(0);
            }
            return _string;
        }

        public static (T, T, List<T>)? GetDifference<T>(this IEnumerable<T> datas)
        {
            return GetDifference<T>([.. datas]);
        }

        public static (T, T, List<T>)? GetDifference<T>(this IList<T> datas)
        {
            int index = 0;
            int common_place = -1;
            var conver = TypeDescriptor.GetConverter(typeof(T));
            foreach (var data in datas)
            {
                if (index > 0)
                {
                    T p_element = datas.ElementAt(index - 1);
                    T c_element = datas.ElementAt(index);
                    //Logger.DebudElite(data, p_element, c_element);
                    if (p_element is not null && c_element is not null)
                    {
                        string privious = p_element.GetSafetyChar();
                        string current = c_element.GetSafetyChar();
                        for (int place = 0; place < current.Length; place++)
                        {
                            if (current[place] != privious[place])
                            {
                                if (common_place == -1 || common_place > place)
                                {
                                    common_place = place;
                                }
                                break;
                            }
                        }
                    }
                }
                index++;
            }
            if (common_place < datas.Count && common_place > 0)
            {
                List<T> result =
                    [
                        .. datas.Select(x =>
                    {
                        string? value = x?.GetSafetyChar()[common_place..];
                        if (value is not null)
                        {
                            object? _obj = conver.ConvertFromString(value);
                            if (_obj is not null)
                            {
                                return (T)_obj;
                            } else return default;
                        } else return default;
                    })
                    ];
                return (result.First(), result.Last(), result);
            }
            else
            {
                return null;
            }
        }

        public static List<string> MatchTime(this string data, int wall = -1)
        {
            List<string> result = [];
            foreach (Match match in Regex.Matches(data, @"(\d+[^\d\.a-zA-Z0-9]*)+"))
            {
                string getMatch = match.Value;
                if (wall == -1)
                {
                    result.Add(getMatch);
                }
                else
                {
                    if (getMatch.Length >= wall) result.Add(getMatch);
                }
            }
            return result;
        }

        public static List<string> MatchCreateSqlColumns(this string source)
        {
            List<string> columns = [];

            Match mainMatch = Regex.Match(source, "\\((.*)\\)");

            if (mainMatch.Groups.Count > 1)
            {
                foreach (var sec in mainMatch.Groups[1].Value.Split(','))
                {
                    string[] fields = Regex.Split(sec.Trim(), "\\s+");
                    if (fields.Length > 0)
                    {
                        columns.Add(fields[0]);
                    }
                }
            }
            return columns;
        }

        public static string CurrentDir(this string source)
        {
            return Directory.GetCurrentDirectory();
        }

        public static string GetSafetyChar(this object source, bool isRegexp = false)
        {
            if (source != null)
            {
                if (isRegexp)
                {
                    if (source.ToString() is not null and string value)
                    {
                        return value.Replace(@"\\", @"\");
                    }
                }
                else
                {
                    return source?.ToString() ?? string.Empty;
                }
            }
            return string.Empty;
        }

        public static string GetXORcode(this string source)
        {
            byte[] sBytes = Encoding.ASCII.GetBytes(source);
            byte xorByte = 0x00;
            foreach(byte _byte in sBytes)
            {
                xorByte = Convert.ToByte(xorByte ^ _byte);
            }
            return xorByte.ToString();
        }

        public static T? GetSafetyChar<T>(this ObservableCollection<T> source, int index)
        {
            if (source.Count > 0 && source != null)
            {
                if (index >= source.Count)
                {
                    return source[source.Count - 1];
                }
                else
                {
                    return source[index];
                }
            }
            else
            {
                return default;
            } 
        }

        public static string GetSafetyChar(this object[] source, int index)
        {
            if (source?.Length > index)
            {
                return source[index].GetSafetyChar();
            }
            else
            {
                return source?[^1].GetSafetyChar() ?? string.Empty;
            }
        }

        public static string GetSafetyChar(this IEnumerable<object> source, int index)
        {
            if (index >= 0) 
            {
                if (source.Count() == 0)
                {
                    return string.Empty;
                }
                else if (index < source.Count())
                {
                    return source.ElementAt(index).GetSafetyChar();
                }
                else
                {
                    return source.ElementAt(^1)?.GetSafetyChar() ?? string.Empty;
                } 
            } else return string.Empty;
        }

        public static bool CanSafetyConvert(this FieldInfo source, string value, out object? push)
        {
            bool canConvert = false;
            Type type = source.FieldType;
            try
            {
                TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
                if (typeConverter.CanConvertFrom(typeof(string)))
                {
                    if (typeConverter.ConvertFromString(value) is not null and object convert)
                    {
                        push = convert;
                        canConvert = true;
                    }
                    else
                    {
                        push = default;
                    }
                }
                else
                {
                    push = default;
                }
            }
            catch
            {
                push = default;
            }
            return canConvert;
        }

        public static bool CanSafetyConvert(this PropertyInfo source, string value, out object? push)
        {
            bool canConvert = false;
            Type type = source.PropertyType;
            try
            {
                TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
                if (typeConverter.CanConvertFrom(typeof(string)))
                {
                    if (typeConverter.ConvertFromString(value) is not null and object convert)
                    {
                        push = convert;
                        canConvert = true;
                    }
                    else
                    {
                        push = default;
                    }
                }
                else
                {
                    push = default;
                }
            }
            catch
            {
                push = default;
            }
            return canConvert;
        }

        public static void SetGridWidget(this Grid layout, int row, int column, UIElement widget)
        {
            Grid.SetRow(widget, row);
            Grid.SetColumn(widget, column);
            layout.Children.Add(widget);
        }

        public static void SetGridWidget(this Grid layout, int row, int column, int rowspan, int columnspan, UIElement widget)
        {
            Grid.SetRow(widget, row);
            if (rowspan >= 1) Grid.SetRowSpan(widget, rowspan);
            Grid.SetColumn(widget, column);
            if (columnspan >= 1) Grid.SetColumnSpan(widget, columnspan);
            layout.Children.Add(widget);
        }

        public static List<UIElement> GetGridRowWidget(this Grid layout, int row)
        {
            return [..layout.Children.OfType<UIElement>().Where(unit => Grid.GetRow(unit) == row)] ;
        }

        public static ControlType? GetGridRowWidget<ControlType>(this Grid layout, int row, string name)
        {
            ControlType? _Control = default;
            foreach (var unit in layout.Children.OfType<UIElement>().Where(unit => Grid.GetRow(unit) == row))
            {
                if (unit is FrameworkElement Control)
                {
                    if (unit is ControlType keyUI && Control.Name == name)
                    {
                        _Control = keyUI;
                    }
                }
            }
            return _Control;
        }

        public static KeyValuePair<KType, VType>? GetGridRowPairWidget<KType, VType>(this Grid layout, int row, string keyName, string valueName)
        {
            KType? kType = default;
            VType? vType = default;
            foreach (var unit in layout.Children.OfType<UIElement>().Where(unit => Grid.GetRow(unit) == row))
            {
                if (unit is FrameworkElement Control)
                {
                    if (unit is KType keyUI && Control.Name == keyName)
                    {
                        kType = keyUI;
                    }
                    else if (unit is VType valueUI && Control.Name == valueName)
                    {
                        vType = valueUI;
                    }
                }
            }
            if (kType != null && vType != null)
            {
                return new KeyValuePair<KType, VType>(kType, vType);
            }
            else
            {
                return null;
            }
        }

        public static List<UIElement> GetGridWidget(this Grid layout, int column)
        {
            return [..layout.Children.OfType<UIElement>().Select(unit => {
                    if (Grid.GetColumn(unit) == column)
                    {
                        return unit;
                    }
                    return default;
                    })];
        }

        public static T? GetWidget<T>(this List<UIElement> widgets, string key)
        {
            var filter = widgets.Where((widget) => widget is T && ((FrameworkElement)widget).Name == key).ToList();

            if (filter.Count > 0 && filter[0] is T excpect) return excpect;
            return default;
        }

        /// <summary>
        /// 1 - value valid ; 0 - value invalid ; surplus - all
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static List<FieldInfo> GetFields(this object source, int type = 1)
        {

            return [..source.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
                                              .Where(bool (value) => {
                                                  if (type == 1)
                                                  {
                                                    return value.GetValue(source) != null;
                                                  }
                                                  else if (type == 0)
                                                  {
                                                      return value.GetValue(source) == null;
                                                  }
                                                  else{
                                                      return true;
                                                  }
                                              })];
        }

        public static FieldInfo? GetField(this object source, string name) {
            var findField = source.GetFields(2).Where(x => x.Name == name);
            if (findField.Any())
            {
                return findField.ElementAt(0);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 1 - value valid ; 0 - value invalid ; surplus - all
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static List<PropertyInfo> GetPropertys(this object source, int type = 1)
        {

            return [..source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                              .Where(bool (value) => {
                                                  if (type == 1)
                                                  {
                                                    return value.GetValue(source) != null;
                                                  }
                                                  else if (type == 0)
                                                  {
                                                      return value.GetValue(source) == null;
                                                  }
                                                  else{
                                                      return true;
                                                  }
                                              })];
        }

        public static List<PropertyInfo> GetPropertys(this Type source, int type = 1)
        {

            return [..source.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                              .Where(bool (value) => {
                                                  if (type == 1)
                                                  {
                                                    return value.GetValue(source) != null;
                                                  }
                                                  else if (type == 0)
                                                  {
                                                      return value.GetValue(source) == null;
                                                  }
                                                  else{
                                                      return true;
                                                  }
                                              })];
        }

        public static PropertyInfo? GetProperty(this object source, string name)
        {
            var findField = source.GetPropertys(2).Where(x => x.Name == name);
            if (findField.Any())
            {
                return findField.ElementAt(0);
            }
            else
            {
                return null;
            }
        }

        public static T? FindNestVisualTreeObject<T>(this Control source, string name)
        {
            if (source.Template.FindName("PART_EditableTextBox", source) is not null and T obj)
            {
                return obj;
            }
            return default;
        }

        /// <summary>
        /// encording to parant unit find single children visual tree
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="VariantSource"></param>
        /// <returns></returns>
        public static T? FindVisualBtObject<T>(this DependencyObject OriginSource) where T : DependencyObject
        {
            DependencyObject source = OriginSource;
            if (source is T)
            {
                return (T)source;
            }
            else
            {
                T? receive = null;
                LoopForVisualThree<T>(source, ref receive);
                return receive;
            }
        }

        /// <summary>
        /// encording to parent unit find the multi children visual tree
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="G"></typeparam>
        /// <param name="OriginSource"></param>
        /// <returns></returns>
        public static List<G> FindVisualUpObjects<T, G>(this DependencyObject OriginSource) where T : DependencyObject
        {
            List<G> result = [];
            while (OriginSource != null)
            {
                if (OriginSource is T)
                {
                    LoopForVisualThree<T, G>(OriginSource, result);
                    break;
                }
                else
                {
                    OriginSource = VisualTreeHelper.GetParent(OriginSource);
                }
            }
            return result;
        }

        /// <summary>
        /// encording to base unit find the parent visual tree
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="OriginSource"></param>
        /// <returns></returns>
        public static T? FindVisualUpObject<T>(this DependencyObject OriginSource) where T : DependencyObject
        {
            while (OriginSource != null)
            {
                if (OriginSource is T RealSource)
                {
                    return OriginSource as T;
                }
                else
                {
                    OriginSource = VisualTreeHelper.GetParent(OriginSource);
                }
            }
            return null;
        }

        private static void LoopForVisualThree<T, G>(DependencyObject OriginSource, List<G> Receive)
        {
            if (OriginSource != null)
            {
                for (int index = 0; index < VisualTreeHelper.GetChildrenCount(OriginSource); index++)
                {
                    var children = VisualTreeHelper.GetChild(OriginSource, index);
                    if (children is G RealSource)
                    {
                        Receive.Add(RealSource);
                    }
                    else
                    {
                        LoopForVisualThree<T, G>(children, Receive);
                    }
                }
            }
        }

        private static void LoopForVisualThree<T>(DependencyObject OriginSource, ref T? receive)
        {
            if (OriginSource != null)
            {
                for (int index = 0; index < VisualTreeHelper.GetChildrenCount(OriginSource); index++)
                {
                    var children = VisualTreeHelper.GetChild(OriginSource, index);
                    if (children is T RealSource)
                    {
                        receive = RealSource;
                        break;
                    }
                    else
                    {
                        LoopForVisualThree<T>(children, ref receive);
                    }
                }
            }
        }

    }

    public class DenCryptoGraphicManager : IDenCryptoGraphic
    {
        public string DEbase64(string destring)
        {
            var decode = Convert.FromBase64String(destring);
            string replace = Encoding.UTF8.GetString(decode).Replace("_", "/").Replace("&", "+").Replace("#", "=");
            return replace;
        }

        public string DeCryptoGraphic(string crypt, string password)
        {
            byte[] byteCtypt = Convert.FromBase64String(crypt);
            int lenght = byteCtypt.Length;

            byte[] salt = new byte[16];
            byte[] iv = new byte[16];
            byte[] cryptbody = new byte[lenght - 32];
            Array.Copy(byteCtypt, 0, salt, 0, 16);
            Array.Copy(byteCtypt, 16, iv, 0, 16);
            Array.Copy(byteCtypt, 32, cryptbody, 0, lenght - 32);

            Rfc2898DeriveBytes rfc2898DeriveBytes = new 
            (
                password: password,
                salt: salt,
                iterations: 10000,
                hashAlgorithm: HashAlgorithmName.SHA512
            );

            using Aes AES = Aes.Create();

            AES.Mode = CipherMode.CBC;

            AES.Key = rfc2898DeriveBytes.GetBytes(AES.KeySize / 8);

            AES.IV = iv;

            using MemoryStream memoryst = new (cryptbody);

            using CryptoStream cryptst = new (memoryst, AES.CreateDecryptor(), CryptoStreamMode.Read);

            using StreamReader reader = new (cryptst);

            return reader.ReadToEnd();
        }

        public string ENbase64(string enstring)
        {
            byte[] encode = Encoding.UTF8.GetBytes(enstring);
            string replace = Convert.ToBase64String(encode).Replace("/", "_").Replace("+", "&").Replace("=", "#");
            return replace;
        }

        public string EnCryptoGraphic(string content, string password)
        {
            byte[] keys = new byte[16];

            byte[] IValue = new byte[16];

            using (var random = RandomNumberGenerator.Create())
            {
                // fill the random byte in new byte[]
                random.GetBytes(keys); random.GetBytes(IValue);
                
            }

            // prd key
            Rfc2898DeriveBytes rfc2898DeriveBytes = new
            (
                password: password,
                salt: keys,
                iterations: 10000,
                hashAlgorithm: HashAlgorithmName.SHA512
            );

            using var AES = Aes.Create();
            //输出长度：512位(64字节)
            //用途：用于密码派生(PBKDF2)时的哈希计算
            //作用：将密码和盐值转换为固定长度的哈希值
            //与AES的关系：仅用于密钥派生,不影响AES本身的参数
            //AES 标准支持 128位、192位和 256位,取决于用了什么类型的哈希算法
            AES.Mode = CipherMode.CBC;

            AES.Key = rfc2898DeriveBytes.GetBytes(AES.KeySize / 8);
            //固定128位 / 8 16字节
            AES.IV = IValue;

            using MemoryStream menory = new ();

            menory.Write(keys, 0, 16);

            menory.Write(IValue, 0, 16);

            // 创建加密流,参数为 内存流 和已经设置好参数的加密机
            using CryptoStream crypt = new (menory, AES.CreateEncryptor(), CryptoStreamMode.Write);

            crypt.Write(Encoding.UTF8.GetBytes(content), 0, content.Length);

            crypt.Close();

            string cryptReulst = Convert.ToBase64String(menory.ToArray());

            return cryptReulst;

        }
    }

    public class ActivityDetetorAssistServer : IActivityDetetorAssist
    {
        readonly IDatabaseServer DatabaseServer;

        public ActivityDetetorAssistServer()
        {
            App MyApp = (App)Application.Current;
            DatabaseServer = MyApp.RetailSection.GetRequiredService<IDatabaseServer>();
        }

        public async Task<object?> AutoFindDim(string content, string flag)
        {
            if (DatabaseServer.GetIdentify())
            {
                return await WorkOrder_CloudDimMatch(content, flag);
            }
            else
            {
                return await WorkOrder_LocalDimMatch(content, flag);
            }
        }

        public async Task<Supabase.Postgrest.Responses.ModeledResponse<WorkOrderUnfold>?> WorkOrder_CloudDimMatch(string content, string flag)
        {
            Supabase.Postgrest.Responses.ModeledResponse<WorkOrderUnfold>? unfoldResponse = null;

            if (DatabaseServer.GetCloudDB() is not null and Supabase.Client CloudDB)
            {
                switch (flag)
                {
                    case "IMEI":
                        unfoldResponse = await CloudDB.From<WorkOrderUnfold>()
                        .Filter("imei", Supabase.Postgrest.Constants.Operator.Like, $"%{content}%")
                        .Select(x => new object[] { x.Sn, x.Id, x.Imei, x.BlueMac })
                        .Get();
                        break;
                    case "SN":
                        unfoldResponse = await CloudDB.From<WorkOrderUnfold>()
                        .Filter("sn", Supabase.Postgrest.Constants.Operator.Like, $"%{content}%")
                        .Select(x => new object[] { x.Sn, x.Id, x.Imei, x.BlueMac })
                        .Get();
                        break;
                    case "BLUE":
                        unfoldResponse = await CloudDB.From<WorkOrderUnfold>()
                        .Filter("blue_mac", Supabase.Postgrest.Constants.Operator.Like, $"%{content}%")
                        .Select(x => new object[] { x.Sn, x.Id, x.Imei, x.BlueMac })
                        .Get();
                        break;
                    case "DOID":
                        unfoldResponse = await CloudDB.From<WorkOrderUnfold>()
                        .Filter("id", Supabase.Postgrest.Constants.Operator.Like, $"%{content}%")
                        .Select(x => new object[] { x.Sn, x.Id, x.Imei, x.BlueMac })
                        .Get();
                        break;
                }
            }
            if (unfoldResponse?.Models.Count > 0)
            {
                return unfoldResponse;
            }
            else
            {
                return null;
            }
        }

        public async Task<Dictionary<string, List<object>>?> WorkOrder_LocalDimMatch(string content, string flag)
        {
            if (DatabaseServer.LocalDBSurvival(DBreserveInfo.LocalDBName))
            {
                string Sql = string.Empty;

                SqliteExceuteResult? exceuteResult = null;

                switch (flag)
                {
                    case "IMEI":
                        Sql = $"SELECT sn,imei,id,blue_mac FROM workorder_unfold WHERE imei LIKE '%{content}%'";
                        exceuteResult = await DatabaseServer.LocalDBQueryFree(Sql);
                        break;
                    case "SN":
                        Sql = $"SELECT sn,imei,id,blue_mac FROM workorder_unfold WHERE sn LIKE '%{content}%'";
                        exceuteResult = await DatabaseServer.LocalDBQueryFree(Sql);
                        break;
                    case "BLUE":
                        Sql = $"SELECT sn,imei,id,blue_mac FROM workorder_unfold WHERE blue_mac LIKE '%{content}%'";
                        exceuteResult = await DatabaseServer.LocalDBQueryFree(Sql);
                        break;
                    case "DOID":
                        Sql = $"SELECT sn,imei,id,blue_mac FROM workorder_unfold WHERE id LIKE '%{content}%'";
                        exceuteResult = await DatabaseServer.LocalDBQueryFree(Sql);
                        break;
                }

                if (exceuteResult is not null && 
                    exceuteResult.ErrorHint is null &&
                    exceuteResult.Result.Count > 0 && 
                    exceuteResult.Result.TryGetValue(Sql, out var tableDataMap))
                {
                    return tableDataMap;
                }
            }
            return null;
        }
    }

}